package com.yunji.hushuang.dynamicproxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author hushuang
 * @since 2019/10/14
 */
public class MyProxy {

    public static final String LN = "\r\n";

    // 返回代理对象
    public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, InvocationHandler invocationHandler) {

        try {
            // 生成 Java 源码
            String source = generateSrc(interfaces);
            // 将源码输出到 Java 文件
            File sourceFile = saveSrc(source);
            // 将 Java 文件编译成 class 文件
            compile(sourceFile);
            // 将 class 文件加载到 jvm
            Class<?> clazz = classLoader.findClass("$Proxy0");
            // 返回代理对象
            return clazz.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
        } catch (Exception e) {
            throw new RuntimeException("Create Proxy Failed", e);
        }
    }

    private static void compile(File sourceFile) throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        try (StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null)) {
            Iterable iterable = manage.getJavaFileObjects(sourceFile);
            JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);
            task.call();
        } finally {
            sourceFile.delete();
        }
    }

    private static File saveSrc(String source) throws IOException {
        File sourceFile = new File(MyProxy.class.getResource("").getPath() + "$Proxy0.java");
        try (FileWriter fileWriter = new FileWriter(sourceFile)) {
            fileWriter.write(source);
            fileWriter.flush();
        }
        return sourceFile;
    }

    private static String generateSrc(Class[] interfaces) {
        StringBuilder sb = new StringBuilder();
        sb.append("package com.yunji.hushuang.dynamicproxy;").append(LN)
                .append(LN)
                // 引包
                .append("import java.lang.reflect.Method;").append(LN)
                .append("import java.lang.reflect.InvocationHandler;").append(LN)
                .append(LN)
                // 创建类, 实现接口
                .append("public class $Proxy0 implements ").append(interfaces[0].getName()).append(" {").append(LN);

        // 字段
        sb.append("private InvocationHandler h;").append(LN)
                // 构建方法
                .append("public $Proxy0(InvocationHandler h) {").append(LN)
                .append("this.h = h;").append(LN)
                .append("}").append(LN);

        for (Class inter : interfaces) {
            for (Method m : inter.getMethods()) {
                sb.append("@Override").append(LN)
                        .append("public ").append(m.getReturnType()).append(" ").append(m.getName()).append("() {").append(LN)

                        // 调用代理方法，并捕获异常
                        .append("try {").append(LN)
                        .append("Method m = ").append(interfaces[0].getName()).append(".class.getMethod(\"").append(m.getName()).append("\", new Class[0]);").append(LN)
                        .append("h.invoke(this, m, new Object[0]);").append(LN)
                        .append("} catch (Throwable e) {").append(LN)
                        .append("e.printStackTrace();").append(LN)
                        .append("}").append(LN)
                        .append("}").append(LN);
            }
            sb.append(LN);
        }
        sb.append("}").append(LN);

        return sb.toString();
    }
}
